package com.bestcem.xm.member.handler.imports.easyexcel;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.bestcem.xm.common.core.domain.web.ServiceResult;
import com.bestcem.xm.common.core.exception.InvalidHeaderExcpetion;
import com.bestcem.xm.common.core.uitls.DateUtil;
import com.bestcem.xm.common.core.uitls.EnumUtil;
import com.bestcem.xm.member.constant.ColumnConstant;
import com.bestcem.xm.member.constant.Constants;
import com.bestcem.xm.member.constant.ExcelFieldConstants;
import com.bestcem.xm.member.entity.mongo.ExtendFieldValue;
import com.bestcem.xm.member.entity.mysql.LabelValueId;
import com.bestcem.xm.member.entity.pojo.ExtendFieldEnumItemDO;
import com.bestcem.xm.member.entity.pojo.MemberDO;
import com.bestcem.xm.member.enums.ExtendFieldTypeEnum;
import com.bestcem.xm.member.enums.ImportRecordStatusEnum;
import com.bestcem.xm.member.enums.MemberStatusEnum;
import com.bestcem.xm.member.handler.imports.ImportExcelUtil;
import com.bestcem.xm.member.service.ImportService;
import com.bestcem.xm.member.service.MemberService;
import com.bestcem.xm.member.service.dto.FieldDTO;
import com.bestcem.xm.member.service.dto.LabelDTO;
import com.bestcem.xm.member.service.dto.LabelValueDTO;
import com.bestcem.xm.member.service.param.ImportMemberParam;
import com.bestcem.xm.member.util.CheckUtil;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import java.util.*;
import java.util.stream.Collectors;

import static com.bestcem.xm.member.constant.Constants.*;
import static com.bestcem.xm.member.handler.imports.ImportExcelUtil.*;

/**
 * 解析个人联系人数据
 *
 * @author yf.wang <yf.wang@idiaoyan.com>
 * @version V1.0
 * @date 2021/7/22
 */
@Slf4j
@Getter
@Setter
public class HttpReadIndividualMemberListener extends AnalysisEventListener<Map<Integer, String>> {

    /**
     * 参数
     */
    private ImportMemberParam param;

    /**
     * MemberService
     */
    private MemberService memberService;

    /**
     * ImportService
     */
    private ImportService importService;

    /**
     * 更新进度的频率
     */
    private Integer per;

    /**
     * excel数据总数量
     */
    private Integer count;

    /**
     * 是否已经获取总数量
     */
    private boolean isGetCount;

    /**
     * 是否终止读取excel
     */
    private Boolean isStop;

    /**
     * 错误消息
     */
    private JSONArray errorMsgJsons;

    /**
     * 剩余的联系人数量
     */
    private Integer freeCount;

    /**
     * 表头
     */
    private List<String> headers;

    /**
     * excel 异常处理
     *
     * @param e
     * @param analysisContext
     * @throws Exception
     */
    @Override
    public void onException(Exception e, AnalysisContext analysisContext) throws Exception {
        if (log.isInfoEnabled()) {
            log.info("执行失败 {}", e.getMessage());
        }
        // 设置进度条结束
        // importService.deleteProgressBarStatus(0, param.getImportRecordId());
        // 继续抛出异常
        throw e;
    }

    @Override
    public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
        // 获取表头数据
        headers = new ArrayList<>();
        headMap.forEach((k, v) -> {
            if (StrUtil.isNotEmpty(v)) {
                headers.add(v);
            }
        });
        // 校验表头
        Set<String> headerSet = new HashSet<>(headers);
        if(StrUtil.isEmpty(param.getPrimaryKey())){
            throw new InvalidHeaderExcpetion("导入主键类型为空");
        }

        switch (param.getPrimaryKey()){
            case ColumnConstant.Member.MOBILE:
            case ColumnConstant.Member.EMAIL:
            case ColumnConstant.Member.OUT_NO:
                String primaryKeyName = PRIMARY_KEY_MAP.getOrDefault(param.getPrimaryKey(), "");
                if (!headers.contains(primaryKeyName)) {
                    // 主键列不存在
                    throw new InvalidHeaderExcpetion(String.format("主键列 %s 不存在表头 %s 中", primaryKeyName, headerSet.toString()));
                }
                break;
            case Constants.WECHAT_IMPORT:
                String appIdHead = PRIMARY_KEY_MAP.get(ColumnConstant.Member.APP_ID);
                String openIdHead = PRIMARY_KEY_MAP.get(ColumnConstant.Member.OPEN_ID);
                if (!headers.contains(appIdHead) || !headers.contains(openIdHead)) {
                    // 主键列不存在
                    throw new InvalidHeaderExcpetion(String.format("主键列 %s+%s 不存在表头 %s 中", appIdHead, openIdHead, headerSet.toString()));
                }
                break;
            default:
                throw new InvalidHeaderExcpetion(String.format("导入主键类型：%s 异常", param.getPrimaryKey()));
        }

        if (headers.size() != headerSet.size()) {
            // 列名不唯一
            throw new InvalidHeaderExcpetion("列名不唯一");
        }
    }

//    /**
//     * 获取表头数据
//     *
//     * @param map
//     * @param analysisContext
//     */
//    @Override
//    public void invokeHead(Map<Integer, CellData> map, AnalysisContext analysisContext) {
//        // 获取表头数据
//        headers = new ArrayList<>();
//        map.forEach((k, v) -> {
//            if (StrUtil.isNotEmpty(v.getStringValue())) {
//                headers.add(v.getStringValue());
//            }
//        });
//        // 校验表头
//        Set<String> headerSet = new HashSet<>(headers);
//        String primaryKeyName = PRIMARY_KEY_MAP.getOrDefault(param.getPrimaryKey(), "");
//        if (!headers.contains(primaryKeyName)) {
//            // 主键列不存在
//            throw new InvalidHeaderExcpetion(String.format("主键列 %s 不存在表头 %s 中", primaryKeyName, headerSet.toString()));
//        }
//        if (headers.size() != headerSet.size()) {
//            // 列名不唯一
//            throw new InvalidHeaderExcpetion("列名不唯一");
//        }
//    }


    /**
     * 这个每一条数据解析都会来调用
     *
     * @param data
     * @param analysisContext
     */
    @Override
    public void invoke(Map<Integer, String> data, AnalysisContext analysisContext) {
        if (!isGetCount) {
            // 判断是否有表头数据，没有直接结束
            if (CollUtil.isEmpty(headers)) {
                throw new InvalidHeaderExcpetion("没有表头数据");
            }

            // 若还没有获取总行数
            // 获取总行数
            Integer approximateTotalRowNumber =
                    analysisContext.readSheetHolder().getApproximateTotalRowNumber() - 1;
            count = approximateTotalRowNumber;
            if (log.isInfoEnabled()) {
                log.info("共有 {} 条数据", count);
            }
            if (approximateTotalRowNumber == 0) {
                // 没有数据 则终止读取excel
                isStop = true;
                return;
            }
            // 获取更新记录的频率
            per = importService.getCalcUpdateProgressStep(count);
            // 初始化进度
            importService.setProgressBarStatus(0, param.getImportRecordId(), 0, count);
            // 设置 读取总数量为true
            isGetCount = true;
        }
        // 获取当前解析的行数
        Integer rowIndex = analysisContext.readRowHolder().getRowIndex();
        // fix 20211028 数据所在的行数
        Integer row = rowIndex + 1;

        ServiceResult<String> handleResult = handle(data, rowIndex);
        if (!handleResult.isSuccess()) {
            // 添加错误信息
            JSONObject messageJson = new JSONObject();
            // 添加每一行的错误信息
            messageJson.put(row.toString(), handleResult.getMsg());
            errorMsgJsons.add(messageJson);
        }
        if (rowIndex % per == 0) {
            // 每per条更新一次进度
            if (log.isInfoEnabled()) {
                log.info("每 {} 条更新一次进度, 当前行数 {}", per, rowIndex);
            }
            importService.setProgressBarStatus(0, param.getImportRecordId(), rowIndex, count);
        }
    }

    @Override
    public void doAfterAllAnalysed(AnalysisContext context) {
        // 由于hasNext方法被重写,不一定能走到该方法
        if (!isStop) {
            finalOperation();
        }
    }

    /**
     * easyExcel 处理完所有数据之后,执行的后置操作
     *
     * @return void
     * @author zhangfeng <feng.zhang@idiaoyan.com>
     * @date 2021/8/31 15:42
     */
    private void finalOperation() {
        //设置进度条结束
        importService.setProgressBarStatus(0, param.getImportRecordId(), count, count);
        int failedCount = errorMsgJsons.size();
        JSONObject dataJson = new JSONObject();
        dataJson.put("receiveCount", count);
        dataJson.put("successCount", count - failedCount);
        dataJson.put("failedCount", failedCount);
        dataJson.put("total_error_msg", errorMsgJsons);
        // 更新导入进度
        if (failedCount == count) {
            // 全部失败 设置记录为失败
            importService.updateImportRecordData(param.getImportRecordId(), dataJson, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_FAILED.getIndex());
        } else {
            // 部分失败
            importService.updateImportRecordData(param.getImportRecordId(), dataJson, ImportRecordStatusEnum.IMPORT_RECORD_STATUS_SUCCESS.getIndex());
        }
    }

    /**
     * 处理数据
     *
     * @param data
     * @param rowIndex
     * @return
     */
    private ServiceResult<String> handle(Map<Integer, String> data, Integer rowIndex) {
        // 将excel 数据转换为json
        JSONObject memberJson = buildMemberJson(data, headers, INDIV_FIELD_NAME_AND_FIELD_MAP);

        String errorMessage = "";
        // 校验数据
        errorMessage = checkErrorMsg(memberJson, rowIndex);
        if (StrUtil.isNotEmpty(errorMessage)) {
            // 若数据校验失败 则退出
            return ServiceResult.fail(errorMessage);
        }
        // 构建个人联系人数据
        MemberDO member = buildMember(memberJson, param);

        // 联系人标签
        List<LabelValueId> memberLabels = new ArrayList<>();
        // 联系人扩展字段
        Map<String, ExtendFieldValue> fieldValList = new HashMap<>();
        Map<String, Boolean> updateFieldsIsNullMap = new HashMap<>();
        // 检查扩展字段与标签
        errorMessage = extendFieldLabel(memberJson, memberLabels, fieldValList, updateFieldsIsNullMap);
        if (StrUtil.isNotEmpty(errorMessage)) {
            return ServiceResult.fail(errorMessage);
        }
        // 保存联系人
        ServiceResult<Void> saveResult =
                save(member, memberLabels, fieldValList, updateFieldsIsNullMap);
        if (!saveResult.isSuccess()) {
            log.error(saveResult.getMsg());
            if ("已达套餐限额".equals(saveResult.getMsg())) {
                return ServiceResult.fail(saveResult.getMsg());
            }
            return ServiceResult.fail("未知错误");
        }
        return ServiceResult.success();
    }

    /**
     * 校验数据
     *
     * @param memberJson
     * @param rowIndex
     */
    private String checkErrorMsg(JSONObject memberJson, Integer rowIndex) {
        StringBuilder errMessage = new StringBuilder();

        // 校验性别
        String gender = memberJson.getString(ExcelFieldConstants.IND_GENDER);
        if (StrUtil.isNotEmpty(gender) && !MEMBER_GENDER.contains(gender)) {
            errMessage.append("性别格式错误 ");
        }

        // 校验手机号
        String mobile = memberJson.getString(ExcelFieldConstants.IND_MOBILE);
        if (StrUtil.isNotEmpty(mobile)) {
            ServiceResult<String> result = CheckUtil.checkMobilePhoneFormat(mobile);
            if (!result.isSuccess()) {
                errMessage.append("手机号格式错误 ");
            }
        }

        // 校验邮箱
        String email = memberJson.getString(ExcelFieldConstants.IND_EMAIL);
        if (StrUtil.isNotEmpty(email)) {
            ServiceResult<String> result = CheckUtil.checkEmailFormat(email);
            if (!result.isSuccess()) {
                errMessage.append("邮箱格式错误 ");
            }
        }

        // 校验外部编号
        String outNo = memberJson.getString(ExcelFieldConstants.IND_OUT_NO);
        if (StrUtil.isNotEmpty(outNo) && outNo.length() > 30) {
            errMessage.append("外部编号长度超出限制 ");
        }


        // 校验生日
        String birthday = memberJson.getString(ExcelFieldConstants.IND_BIRTHDAY);
        if (StrUtil.isNotEmpty(birthday)) {
            ServiceResult<String> result = CheckUtil.checkDateTimeFormat(birthday, "yyyy/M/d");
            if (!result.isSuccess() || !checkDateValue(birthday)) {
                errMessage.append("生日格式错误 ");
            }
        }

        // 校验年龄
        Integer age = memberJson.getInteger(ExcelFieldConstants.IND_AGE);
        if (ObjectUtil.isNotEmpty(age)) {
            if (age < 0 || age > 200) {
                errMessage.append("出生日期超出限制 ");
            }
        }

        // 手机号码、邮箱、外部编号、微信APPID，微信OPENID，支付宝ID至少出现一个
        String openId = memberJson.getString(ExcelFieldConstants.IND_OPEN_ID);
        String aliPayUserId = memberJson.getString(ExcelFieldConstants.IND_ALI_PAY_USER_ID);
        String appId = memberJson.getString(ExcelFieldConstants.IND_APP_ID);
        if (StrUtil.isEmpty(mobile) && StrUtil.isEmpty(email) && StrUtil.isEmpty(outNo)
                && (StrUtil.isEmpty(openId) || StrUtil.isEmpty(appId)) && StrUtil.isEmpty(aliPayUserId)) {
            errMessage.append("手机号码、邮箱、外部编号、微信APPID+微信OPENID，支付宝ID至少出现一个 ");
        }

        if (StrUtil.isEmpty(appId)^StrUtil.isEmpty(openId)) {
            errMessage.append("微信appID+openID必须同时存在");
        }

        if(StringUtils.hasText(appId) && StringUtils.hasText(openId) && (appId.length() > 128 || openId.length() > 128)){
            errMessage.append("微信appID、openID长度不能超过128 ");
        }

        // 检查主键列
        if(StrUtil.isEmpty(param.getPrimaryKey())){
            return "主键参数异常 ";
        }
        switch (param.getPrimaryKey()){
            case ColumnConstant.Member.MOBILE:
            case ColumnConstant.Member.EMAIL:
            case ColumnConstant.Member.ALIPAY_USER_ID:
            case ColumnConstant.Member.OUT_NO:
                // 检查主键列
                if (StrUtil.isEmpty(memberJson.getString(PRIMARY_KEY_MAP.get(param.getPrimaryKey())))) {
                    return "主键不存在 ";
                }
                break;
            case Constants.WECHAT_IMPORT:
                if(!StringUtils.hasText(appId) || !StringUtils.hasText(openId)){
                    return "AppID和OpenID必须同时存在 ";
                }
                break;
            default:
                return "主键参数异常 ";
        }

        // 校验门店
        String store = memberJson.getString(ExcelFieldConstants.IND_STORE);
        if (StrUtil.isNotEmpty(store) && CollUtil.isNotEmpty(param.getStoreNameIdMap())) {
            if (!param.getStoreNameIdMap().containsKey(store)) {
                errMessage.append("所属组织不存在 ");
            }
        }

        log.info(" 第 {} 行 {}", rowIndex, errMessage.toString());
        return errMessage.toString();
    }

    /**
     * 构建联系人数据
     *
     * @param memberJson
     * @param param
     * @return
     */
    private MemberDO buildMember(JSONObject memberJson, ImportMemberParam param) {
        // 获取与主键信息一致的联系人数据
        // 因为主键一定存在 所以就没有实现没有主键的步骤
        MemberDO member;
        if(Constants.WECHAT_IMPORT.equals(param.getPrimaryKey())) {
            member = memberService.findWechatMember(param.getOrgId(), param.getAttribute(),
                    memberJson.getString(PRIMARY_KEY_MAP.get(ColumnConstant.Member.APP_ID)),
                    memberJson.getString(PRIMARY_KEY_MAP.get(ColumnConstant.Member.OPEN_ID)));
        }else{
            member = memberService.queryMember(param.getOrgId(), param.getAttribute(), param.getPrimaryKey(), memberJson.getString(PRIMARY_KEY_MAP.get(param.getPrimaryKey())));
        }
        // 获取联系人序号
        String memberNo = memberService.getMemberNo(param.getOrgId(), param.getAttribute());
        String birthdayStr = memberJson.getString(ExcelFieldConstants.IND_BIRTHDAY);
        JSONArray address = getAddress(memberJson);
        if (ObjectUtil.isNotEmpty(member)) {
            // 存在，则更新
            member.setOrgId(param.getOrgId());
            member.setSysId("member");
            member.setSource("excel");
            member.setName(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_NAME), member.getName(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setNickname(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_NICKNAME), member.getNickname(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setEmail(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_EMAIL), member.getEmail(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setPhone(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_MOBILE), member.getPhone(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setGender(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_GENDER), member.getGender(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setAppId(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_APP_ID), member.getAppId(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setOpenId(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_OPEN_ID), member.getOpenId(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setAliPayUserId(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_ALI_PAY_USER_ID), member.getAliPayUserId(), param.getExistedUpdate(), param.getNullUpdate()));
            if (CollUtil.isNotEmpty(param.getStoreNameIdMap())) {
                member.setStoreId(dealUpdateFieldByString(param.getStoreNameIdMap().get(memberJson.getString(ExcelFieldConstants.IND_STORE)), member.getStoreId(), param.getExistedUpdate(), param.getNullUpdate()));
            } else {
                member.setStoreId(dealUpdateFieldByString("", member.getStoreId(), param.getExistedUpdate(), param.getNullUpdate()));
            }
            member.setAge(ImportExcelUtil.dealUpdateFieldByInteger(memberJson.getInteger(ExcelFieldConstants.IND_AGE), member.getAge(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setBirthday(ImportExcelUtil.dealUpdateFieldByDate(birthdayStr, member.getBirthday(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setAddress(ImportExcelUtil.dealUpdateFieldByString(Optional.ofNullable(address).map(String::valueOf).orElse(null), member.getAddress(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setMemberNo(dealUpdateFieldByString(memberNo, member.getMemberNo(), param.getExistedUpdate(), param.getNullUpdate()));
            member.setOutNo(dealUpdateFieldByString(memberJson.getString(ExcelFieldConstants.IND_OUT_NO), member.getOutNo(), param.getExistedUpdate(), param.getNullUpdate()));
        } else {
            // 不存在 则插入
            member = new MemberDO();
            member.setOrgId(param.getOrgId());
            member.setSysId("member");
            member.setSource("excel");
            member.setName(memberJson.getString(ExcelFieldConstants.IND_NAME));
            member.setNickname(memberJson.getString(ExcelFieldConstants.IND_NICKNAME));
            member.setEmail(memberJson.getString(ExcelFieldConstants.IND_EMAIL));
            member.setPhone(memberJson.getString(ExcelFieldConstants.IND_MOBILE));
            member.setGender(memberJson.getString(ExcelFieldConstants.IND_GENDER));
            member.setAppId(memberJson.getString(ExcelFieldConstants.IND_APP_ID));
            member.setOpenId(memberJson.getString(ExcelFieldConstants.IND_OPEN_ID));
            member.setAliPayUserId(memberJson.getString(ExcelFieldConstants.IND_ALI_PAY_USER_ID));
            if (CollUtil.isNotEmpty(param.getStoreNameIdMap())) {
                member.setStoreId(param.getStoreNameIdMap().getOrDefault(memberJson.getString(ExcelFieldConstants.IND_STORE), ""));
            } else {
                member.setStoreId("");
            }
            member.setAge(memberJson.getInteger(ExcelFieldConstants.IND_AGE));
            if (StrUtil.isNotBlank(birthdayStr)) {
                member.setBirthday(DateUtil.convertStringToDate(birthdayStr, "yyyy/M/d"));
            } else {
                member.setBirthday(null);
            }
            member.setMemberNo(memberNo);
            member.setAddress(Optional.ofNullable(address).map(String::valueOf).orElse(null));
            member.setOutNo(memberJson.getString(ExcelFieldConstants.IND_OUT_NO));
        }
        member.setMemberType(param.getAttribute());
        // member.setNickname("");
        return member;
    }

    /**
     * 扩展字段 和 标签
     *
     * @param memberJson
     * @param fieldValList
     * @param updateFieldsIsNullMap
     */
    private String extendFieldLabel(JSONObject memberJson, List<LabelValueId> memberLabels,
                                    Map<String, ExtendFieldValue> fieldValList,
                                    Map<String, Boolean> updateFieldsIsNullMap) {
        // 获取除基础字段之外的其中有字段（扩展字段与标签字段）
        StringBuilder stringBuilder = new StringBuilder("");
        // 获取除基础字段之外的字段
        Collection<String> extendHeaders = CollUtil.subtract(headers, INDIVIDUAL_BASE_EXCEL_HEADERS);
        if (CollUtil.isNotEmpty(extendHeaders)) {
            //循环这些字段
            extendHeaders.forEach(extendHeader -> {
                // 获取excel中该字段的值
                String field = memberJson.getString(extendHeader);
                // 如果存在该标签 且是可手动打标签的
                Map<String, LabelDTO> labelMap = param.getLabelMap();
                Map<String, FieldDTO> fieldMap = param.getFiledMap();
                if (CollUtil.isNotEmpty(labelMap) && labelMap.containsKey(extendHeader)) {
                    // 获取该标签对用的相关信息（标签是否可手动修改 标签值信息）
                    LabelDTO labelDTO = labelMap.get(extendHeader);
                    if (ObjectUtil.isNotEmpty(labelDTO) && labelDTO.getManual()) {
                        // 该标签存在且可手动修改
                        // 获取该标签对应的信息
                        // 获取该标签对应的标签值列表
                        List<LabelValueDTO> values = labelDTO.getValues();
                        if (CollUtil.isNotEmpty(values)) {
                            // 获取 excel中标签值对应的标签信息（有且只有一个）
                            List<LabelValueDTO> filterValues = values.stream().filter(value -> value.getValue().equals(field)).collect(Collectors.toList());
                            if (CollUtil.isNotEmpty(filterValues)) {
                                LabelValueDTO labelValueDTO = filterValues.get(0);
                                LabelValueId memberLabel = new LabelValueId();
                                memberLabel.setId(labelValueDTO.getId());
                                memberLabel.setLabelId(labelDTO.getId());
                                memberLabels.add(memberLabel);
                            }
                        }
                    }
                } else if (CollUtil.isNotEmpty(fieldMap) && fieldMap.containsKey(extendHeader)) {
                    // 获取该扩展字段对应的字段信息
                    FieldDTO fieldDTO = fieldMap.get(extendHeader);
                    // 扩展字段id
                    String id = fieldDTO.getId();
                    // 扩展字段类型
                    Integer type = fieldDTO.getType();
                    // 获取excel中该字段的值
                    String value = memberJson.getString(extendHeader);

                    if (StrUtil.isEmpty(id)) {
                        return;
                    }
                    if (StrUtil.isNotEmpty(value) && ObjectUtil.isNotEmpty(type)) {
                        // 如果 存在值 且扩展字段类型也存在 对数据进行校验
                        switch (EnumUtil.getEnumByIndex(ExtendFieldTypeEnum.class, type)) {
                            case ENUM:
                                Map<String, List<ExtendFieldEnumItemDO>> extendFieldEnumMap =
                                        param.getExtendFieldEnumMap();
                                if (CollUtil.isNotEmpty(extendFieldEnumMap)) {
                                    List<ExtendFieldEnumItemDO> extendFieldEnumItems = extendFieldEnumMap.get(id);
                                    if (CollUtil.isEmpty(extendFieldEnumItems)) {
                                        // 该枚举不存在选项
                                        stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                        return;
                                    }
                                    List<String> names = extendFieldEnumItems.stream()
                                            .map(ExtendFieldEnumItemDO::getName).collect(Collectors.toList());
                                    if (CollUtil.isEmpty(names)) {
                                        stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                        return;
                                    }
                                    if (!names.contains(value)) {
                                        stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                        return;
                                    }
                                } else {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 枚举项未在枚举字段值内", extendHeader));
                                    return;
                                }
                                break;
                            case DATETIME:
                                // 检查日期 excel中日期是以yyyy/MM/dd 格式定义的的 但读取出来是 yyyy-MM-dd HH:mm:ss
                                ServiceResult<String> dateResult = CheckUtil.checkDateTimeFormat(value, "yyyy/M/d");
                                if (!dateResult.isSuccess() || !checkDateValue(value)) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 格式错误", extendHeader));
                                    return;
                                }
                                // 修改 日期格式 yyyy/MM/dd
                                value = cn.hutool.core.date.DateUtil.parse(value, "yyyy/M/d")
                                        .toString("yyyy/MM/dd");
                                break;
                            case NUMBER:
                                // 检查数字
                                ServiceResult<String> numberResult = CheckUtil.checkNumberFormat(value);
                                if (!numberResult.isSuccess() || !checkNumValue(value)) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 格式错误", extendHeader));
                                    return;
                                }
                                break;
                            case MOBILE:
                                // 检查手机号
                                ServiceResult<String> mobileResult = CheckUtil.checkMobilePhoneFormat(value);
                                if (!mobileResult.isSuccess()) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 手机号码格式错误", extendHeader));
                                    return;
                                }
                                break;
                            case EMAIL:
                                // 检查邮箱
                                ServiceResult<String> result = CheckUtil.checkEmailFormat(value);
                                if (!result.isSuccess()) {
                                    stringBuilder.append(StrUtil.format("扩展字段:{} 邮箱格式错误", extendHeader));
                                    return;
                                }
                                break;
                            default:
                        }
                    }
                    ExtendFieldValue extendFieldValue = new ExtendFieldValue();
                    extendFieldValue.setFieldType(type);
                    extendFieldValue.setFieldValue(value);
                    extendFieldValue.setFieldName(extendHeader);
                    extendFieldValue.setFieldId(id);
                    fieldValList.put(id, extendFieldValue);
                    // 该 扩展字段的值是否为 null map
                    updateFieldsIsNullMap.put(id, StrUtil.isEmpty(value));
                }
            });
        }
        return stringBuilder.toString();
    }

    private ServiceResult<Void> save(MemberDO member, List<LabelValueId> memberLabels,
                                     Map<String, ExtendFieldValue> fieldValList,
                                     Map<String, Boolean> updateFieldsIsNullMap) {
        if (StrUtil.isNotEmpty(member.getId())) {
            // excel中标签值
            Map<String, List<LabelValueId>> updateMemberLabelMap =
                    memberLabels.stream().collect(Collectors.groupingBy(LabelValueId::getId));
            // 该读者原有的标签值
            final Map<String, List<LabelValueId>> exitMemberLabelMap;
            if (StrUtil.isNotEmpty(member.getLabels())) {
                List<LabelValueId> labelValueIds = JSONObject.parseArray(member.getLabels(), LabelValueId.class);
                if (CollUtil.isNotEmpty(labelValueIds)) {
                    exitMemberLabelMap = labelValueIds.stream().collect(Collectors.groupingBy(LabelValueId::getId));
                } else {
                    exitMemberLabelMap = new HashMap<>();
                }
            } else {
                exitMemberLabelMap = new HashMap<>();
            }

            // 新的标签值
            Set<String> newLabelValueIds = updateMemberLabelMap.keySet();
            // 该联系人原有的标签值
            Set<String> oldLabelValueIds = exitMemberLabelMap.keySet();
            // 最新的标签值
            List<LabelValueId> lastLabel = new ArrayList<>();
            Map<String, LabelDTO> labelMap = param.getLabelMap();
            Map<String, FieldDTO> fieldMap = param.getFiledMap();

            // 循环所有的标签
            labelMap.forEach((name, obj) -> {
                // 获取该标签下所有的标签值id
                List<String> allLabelValueId =
                        obj.getValues().stream().map(LabelValueDTO::getId).collect(Collectors.toList());
                // 获取 新的标签值id 与 该标签下所有的标签值id 的交集
                Collection<String> intersection = CollUtil.intersection(allLabelValueId, newLabelValueIds);
                if (CollUtil.isNotEmpty(intersection)) {
                    // 当系统和导入表均有值 (导入表有该标签的标签值)
                    if (param.getExistedUpdate() && obj.getManual()) {
                        // 更新
                        for (String newLabelId : intersection) {
                            lastLabel.add(updateMemberLabelMap.get(newLabelId).get(0));
                        }
                    } else if (!param.getExistedUpdate() && obj.getManual()) {
                        // 不更新
                        // 获取 该联系人原有标签值id 与 该标签下所有的标签值id 的交集
                        Collection<String> intersectionOldTag = CollUtil.intersection(allLabelValueId, oldLabelValueIds);
                        if (CollUtil.isNotEmpty(intersectionOldTag)) {
                            // 该联系人有该标签的标签值
                            for (String oldLabelId : intersectionOldTag) {
                                lastLabel.add(exitMemberLabelMap.get(oldLabelId).get(0));
                            }
                        }
                    }
                } else {
                    // 该标签导入表里没有
                    if (param.getNullUpdate() && obj.getManual()) {
                        // 更新
                    } else {
                        // 不更新，需要判断该联系人是否已经有该标签下的标签值
                        Collection<String> intersectionOldTag = CollUtil.intersection(allLabelValueId, oldLabelValueIds);
                        if (CollUtil.isNotEmpty(intersectionOldTag)) {
                            for (String oldLabelId : intersectionOldTag) {
                                lastLabel.add(exitMemberLabelMap.get(oldLabelId).get(0));
                            }
                        }

                    }
                }
            });

            // 获取该联系人 原有的扩展字段信息
            String memberFieldValList = member.getFieldValList();
            if (StrUtil.isNotEmpty(memberFieldValList)) {
                // string 转 list
                List<ExtendFieldValue> rawFieldValList = JSONObject.parseArray(memberFieldValList, ExtendFieldValue.class);
                if (CollUtil.isNotEmpty(rawFieldValList)) {
                    rawFieldValList.forEach(field -> {
                        String fieldId = field.getFieldId();
                        if (fieldValList.containsKey(fieldId)) {
                            // 该联系人有这个字段
                            if (updateFieldsIsNullMap.getOrDefault(fieldId, false) && !param.getNullUpdate()) {
                                // 该扩展字段在excel中值为null 且 null不更新
                                fieldValList.put(fieldId, field);
                            } else if (!param.getExistedUpdate()) {
                                // 存在不更新
                                fieldValList.put(fieldId, field);
                            }
                        } else {
                            fieldValList.put(fieldId, field);
                        }
                    });
                }
            }
            member.setLabels(JSONObject.toJSONString(lastLabel));
            if (CollUtil.isNotEmpty(fieldValList)) {
                List<ExtendFieldValue> newFieldValList = new ArrayList<>();
                fieldValList.forEach((k, v) -> {
                    newFieldValList.add(v);
                });
                member.setFieldValList(JSONObject.toJSONString(newFieldValList));
            }
            // 更新时间
            member.setUpdateTime(new Date());
            return memberService.updateImportMember(member);
        } else {
            // 创建
            member.setLabels(JSONObject.toJSONString(memberLabels));
            if (CollUtil.isNotEmpty(fieldValList)) {
                List<ExtendFieldValue> newFieldValList = new ArrayList<>();
                fieldValList.forEach((k, v) -> {
                    // ExtendFieldValue extendFieldValue = buildExtendValue(v);
                    newFieldValList.add(v);
                });
                member.setFieldValList(JSONObject.toJSONString(newFieldValList));
            }
            // 创建时间
            member.setCreateTime(new Date());
            // 更新时间
            member.setUpdateTime(new Date());
            member.setStatus(MemberStatusEnum.ACTIVATED.getIndex());
            return memberService.createImportMember(member, param.getSaveMemberNum());
        }
    }

    /**
     * 重写hasNext 为了实现有问题直接退出
     *
     * @param context
     * @return
     */
    @Override
    public boolean hasNext(AnalysisContext context) {
        if (isStop) {
            // 如果非抛出异常终止,则后续进行的操作
            finalOperation();
        }
        return !isStop;
    }
}
